#ifndef VECTOR4_H
#define VECTOR4_H

#include <cmath>

#include "Vector3.h"


class Vector4
{
public:

    float x;
    float y;
    float z;
	float w;

	Vector4() : x(0.f), y(0.f), z(0.f), w(0.f) {}
	Vector4( float _x, float _y, float _z, float _w ) : x(_x), y(_y), z(_z), w(_w) { }
	Vector4( Vector3 const& v, float _w ) : w(_w) { x=v.x; y=v.y; z=v.z; }



    // functions 
	inline float length(){ return( (float)sqrt( x * x + y * y + z * z + w * w ) ); }
	inline float lengthSquared(){ return( x * x + y * y + z * z + w * w); }
    inline void normalize()
	{
		float l = length();

		x = x / l;
		y = y / l;
		z = z / l;
		w = w / l;
	}

	inline Vector4 normal( )
	{
		float l = length();
		return Vector4( x/l, y/l, z/l, w/l );
	}

	inline Vector4 lerp( const Vector4 & targetLocation, float s )
	{
		return ( *this + s * ( targetLocation - *this ));
	}

	inline float dot( const Vector4 & other )
	{
		return (x * other.x) + (y * other.y) + (z * other.z) + (w * other.w);
	}

	inline Vector4 cross( const Vector4 & other )
	{
		return Vector4((y * other.z) - (z * other.y),
			(z * other.x) - (x * other.z),
			(x * other.y) - (y * other.x), 1);
	}
	inline Vector4 cross( const Vector3 & other )
	{
		return Vector4((y * other.z) - (z * other.y),
			(z * other.x) - (x * other.z),
			(x * other.y) - (y * other.x), 1);
	}


	inline float& operator[](int col)
	{ 
		if( col == 0 )
			return x;
		else if(col == 1)
			return y;
		else if(col == 2)
			return z;
		else if(col == 3)
			return w;
	}

	// operators
	inline Vector4 & operator = ( const Vector4 & other )
	{
		x = other.x;
		y = other.y;
		z = other.z;
		w = other.w;
		return *this;
	}
    inline Vector4 & operator += ( const Vector4 &other )
	{
		x += other.x;
		y += other.y;
		z += other.z;
		w += other.w;
		return *this;
	}
    inline Vector4 & operator -= ( const Vector4 & other )
	{
		x -= other.x;
		y -= other.y;
		z -= other.z;
		w -= other.w;
		return *this;
	}
	inline Vector4 & operator *= ( const Vector4 & other )
	{
		x *= other.x;
		y *= other.y;
		z *= other.z;
		w *= other.w;
		return *this;
	}
	inline Vector4 & operator /= ( const Vector4 & other )
	{
		x /= other.x;
		y /= other.y;
		z /= other.z;
		w /= other.w;
		return *this;
	}

	inline Vector4 & operator += ( float scalar )
	{
		x += scalar;
		y += scalar;
		z += scalar;
		w += scalar;
		return *this;
	}
	inline Vector4 & operator -= ( float scalar )
	{
		x -= scalar;
		y -= scalar;
		z -= scalar;
		w -= scalar;
		return *this;
	}
    inline Vector4 & operator *= ( float scalar )
	{
		x *= scalar;
		y *= scalar;
		z *= scalar;
		w *= scalar;
		return *this;
	}
    inline Vector4 & operator /= ( float scalar )
	{
		x /= scalar;
		y /= scalar;
		z /= scalar;
		w /= scalar;
		return *this;
	}

	// operators against other Vector4
    inline Vector4 operator + ( const Vector4 & other ) const
	{
		return Vector4( x+other.x, y+other.y, z+other.z, w+other.w );
	}
	inline Vector4 operator - ( const Vector4 & other ) const
	{
		return Vector4( x-other.x, y-other.y, z-other.z, w-other.w );
	}
    inline Vector4 operator * ( const Vector4 & other ) const
	{
		return Vector4( x*other.x, y*other.y, z*other.z, w*other.w );
	}
    inline Vector4 operator / ( const Vector4 & other ) const
	{
		return Vector4( x/other.x, y/other.y, z/other.z, w/other.w );
	}

	// operators against scalars
	inline Vector4 operator + ( float scalar ) const
	{
		return Vector4( x+scalar, y+scalar, z+scalar, w+scalar );
	}
	inline Vector4 operator - ( float scalar ) const
	{
		return Vector4( x-scalar, y-scalar, z-scalar, w-scalar );
	}
    inline Vector4 operator * ( float scalar ) const
	{
		return Vector4( x*scalar, y*scalar, z*scalar, w*scalar );
	}
    inline Vector4 operator / ( float scalar ) const
	{
		return Vector4( x/scalar, y/scalar, z/scalar, w/scalar );
	}

	friend inline Vector4 operator + ( const float scalar, const Vector4 & v )
	{
		return Vector4( v.x+scalar, v.y+scalar, v.z+scalar, v.w+scalar );
	}
	friend inline Vector4 operator - ( const float scalar, const Vector4 & v )
	{
		return Vector4( v.x-scalar, v.y-scalar, v.z-scalar, v.w-scalar );
	}
	friend inline Vector4 operator * ( const float scalar, const Vector4 & v )
	{
		return Vector4( v.x*scalar, v.y*scalar, v.z*scalar, v.w*scalar );
	}
	friend inline Vector4 operator / ( const float scalar, const Vector4 & v )
	{
		return Vector4( v.x/scalar, v.y/scalar, v.z/scalar, v.w/scalar );
	}
	


	// self changes Vector4 b = -a;
    inline Vector4 operator + () const { return *this; }
	inline Vector4 operator - () const { return Vector4(-x, -y, -z, -w); }

};

#endif